import java.io.*;
import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.net.*;


public class Node implements iNode {
	
/*	private transient Socket client;
	private transient ServerSocket bootSocket;
	private transient Thread handleIncoming;
	
	public void listen() {
		
		try {
			bootSocket = new ServerSocket(4444);
			System.out.println("Listening");
			
			while (true) {
				client = bootSocket.accept();
				handleIncoming = new Thread(this);
				handleIncoming.start();
			}
		} catch (IOException e) {
			System.out.println("Could not create server socket");
			e.printStackTrace();
		}
	} */
	
	private iNode nextNode;
	private iNode prevNode;
	private String nodeIP;
	private iNode self;
	private String key;
	private String bobIP;
	private String identifier;
	
	public Node(String nodeIP) {
		
		this.nodeIP = nodeIP;
		
		identifier = Hash.hash(nodeIP);
		System.out.println("Identifier for " + nodeIP + " is " + identifier);
		
		try {
			
			self = (iNode) UnicastRemoteObject.exportObject(this, 0);
			Registry reg = LocateRegistry.createRegistry(1099);
			reg.rebind("bootstrap", self);
			System.out.println(self); // print out this node (as an exportable object)
			
		} catch (RemoteException e) {
			System.out.println("Could not a) create bootstrap stub, b) create registry at 1099, or c) bind stub to host");
			e.printStackTrace();
		}
	}
	
	/**
	 * Open a socket to an existing Node (bootstrap) and register this
	 * Node as part of the ring
	 * @param bootIP the IP of the existing Node acting as bootstrap
	 */
	public void join(String bootIP) {

		try {
			
			Registry reg = LocateRegistry.getRegistry(bootIP);
			iNode bootStrap = (iNode) reg.lookup("bootstrap");
			// Don't need to re-export because you already exported this object instance in the constructor
			bootStrap.addNodeToRing(self);
			
		} catch (RemoteException e) {
			System.out.println("Unable to get registry on host");
			e.printStackTrace();
		} catch (NotBoundException e) {
			System.out.println("'bootstrap' not bound on bootstrap server");
			e.printStackTrace();
		}
		
		System.out.println("My next node is now " + nextNode);
		System.out.println("My prev node is now " + prevNode);
	}
	
	public void addNodeToRing(iNode newNode) {
		try {
			newNode.setNext(self);
			newNode.setPrev(self);
		} catch (RemoteException e) {
			System.out.println("Remote Exception");
			e.printStackTrace();
		}
		
	}
	
	public void setNext(iNode next) {
		nextNode = next;
	}
	
	public void setPrev(iNode prev) {
		prevNode = prev;
	}

	public iNode getNext() {
		return nextNode;
	}

	public iNode getPrev() {
		return prevNode;
	}

	public String getID() {
		return null;
	}

	public iNode lookup(String k) {
		return null;
	}

	public void leave() {
	}

	public Object get(String k) {
		return bobIP;
	}

	public void put(String k, Object data) {
		
		key = Hash.hash(k);
//TODO: Really should implement all this in the lookup() method to avoid duplication in put() and get()
		
		// If the key is bigger than identifier for this node, try to store at the next node
		if (key.compareTo(identifier) > 0) {
			
			try {
				String nextIdentifier = nextNode.getID();
				// If I am the biggest node in the Ring, then I will have to store the key here
				if (identifier.compareTo(nextIdentifier) > 0) {
					bobIP = (String) data;
				}
				
				else {
					nextNode.put(key, data);
				}
				
			} catch (RemoteException e1) {
				System.out.println("Problem communicating with nextNode");
				e1.printStackTrace();
			}
		}
		
		// If the key is smaller than this node, check to see if it's bigger than the previous node
		else {
			
			try {
				String prevIdentifier = prevNode.getID();
				// If the key is smaller than the previous node, send it there
				if (prevIdentifier.compareTo(key) > 0) {
					prevNode.put(key, data);
				}
				
				// else I should store it here
				else {
					bobIP = (String) data;
				}
			} catch (RemoteException e) {
				System.out.println("Problem communicating with prevNode");
				e.printStackTrace();
			}
		}

		System.out.println(bobIP);
		System.out.println(key);
	}
	
	@Override
	public String toString() {
		
		return nodeIP;
	}
}
